/*
 * Test.java
 *
 * Created on September 30, 2006, 2:41 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package zh;

import java.awt.geom.Point2D;
import java.util.logging.Level;
import zh.logging.MyLogger;

/**
 * Predicts a targets next location using the assumption he is traveling in a
 * linear manner.
 * @author zheismann
 */
public class LinearTargetingSystem implements TargetingSystem
{
    private static MyLogger logger = MyLogger.getLogger(LinearTargetingSystem.class.getName());
    static { logger.setEnabled(true); logger.setLevel(Level.FINER);}
    
     
    /**
     * Predict the location the target will be at using a specified firing power.  
     * Based upon the time the target will be traveling on a given heading at 
     * a given veloctiy, determine its location.
     */
    public Point2D predictNewLocation(Point2D myCurLocation, Point2D targetCurLocation, double targetsHeading, double targetsVelocity, double powerOfBullet)
    {
        double time = determineTimeToHitTarget(myCurLocation, targetCurLocation, targetsHeading, targetsVelocity, powerOfBullet);
        return predictNewLocation(targetCurLocation, targetsHeading, targetsVelocity, time);
    }
    
    private Point2D predictNewLocation(Point2D targetCurLocation, double targetsHeading, double targetsVelocity, double time)
    {
        double x = targetCurLocation.getX() + ((targetsVelocity) * time * Math.sin(Math.toRadians(targetsHeading)));
        double y = targetCurLocation.getY() + ((targetsVelocity) * time * Math.cos(Math.toRadians(targetsHeading)));
        logger.log("predictNewLocation ("+targetsVelocity+" * " + time+" * Math.sin(Math.toRadians("+targetsHeading+"))): " + (Math.abs(targetsVelocity) * time * Math.sin(Math.toRadians(targetsHeading))) + " : " + x);
        logger.log("predictNewLocation ("+targetsVelocity+" * " + time+" * Math.cos(Math.toRadians("+targetsHeading+"))): " + (Math.abs(targetsVelocity) * time * Math.cos(Math.toRadians(targetsHeading))) + " : " + y);
        return new Point2D.Double(x,y);
    }
    
    private double determineTimeToHitTarget(
        Point2D myCurLocation, 
        Point2D targetCurLocation, 
        double targetsHeading, 
        double targetsVelocity, 
        double powerOfBullet)
    {
        double velocityOfBullet = velocityOfBullet(powerOfBullet);
        double currTime = 2.0;
        double lastTime = 1.0;
        int iterationCount = 0;

        double lastBulletTargetDiff = getDiffToPredictedLocationAndBulletsTraveledDist(myCurLocation, targetCurLocation, targetsHeading, targetsVelocity, velocityOfBullet,lastTime);
        
        logger.log(Level.FINE, iterationCount + "  currTime: " + currTime + "  lastTime: " + lastTime + "  lastBulletTargetDiff: " + lastBulletTargetDiff);
        while ((Math.abs(currTime - lastTime) >= 0.005) && (iterationCount < 15))
        {
            iterationCount++;
            double currBulletTargetDiff = getDiffToPredictedLocationAndBulletsTraveledDist(myCurLocation, targetCurLocation, targetsHeading, targetsVelocity, velocityOfBullet,currTime);
            logger.log(Level.FINE, "\tcurrBulletTargetDiff: " + currBulletTargetDiff);
            if ((currBulletTargetDiff - lastBulletTargetDiff) == 0.0)
            {
                break;
            }
            
            double nextTime = currTime - currBulletTargetDiff*(currTime-lastTime)/(currBulletTargetDiff-lastBulletTargetDiff);
            
            logger.log(Level.FINE, currTime+" - "+currBulletTargetDiff+"*("+currTime+"-"+lastTime+")/("+currBulletTargetDiff+"-"+lastBulletTargetDiff+")");
            
            lastTime = currTime;
            currTime = nextTime;
            lastBulletTargetDiff = currBulletTargetDiff;
            logger.log(Level.FINE, iterationCount + "  currTime: " + currTime + "  lastTime: " + lastTime + "  lastBulletTargetDiff: " + lastBulletTargetDiff);
            logger.log(Level.FINE,"(Math.abs("+currTime+" - "+lastTime+") >= 0.005): " + (Math.abs(currTime - lastTime) >= 0.005));
        }
        logger.log(Level.FINE, "RETURN currTime: " + currTime);
        return currTime;
    }

    
    /**
     * Determine the difference between the distance to the targets predicted
     * location and myrobot and the bullets travel distance in that same time period.
     * We're aiming (no pun intended) to find a difference of zero.
     * @return the difference of 
     * DIST_FROM_MYROBOT2TARGETS_PREDICTED_LOCATION - BULLETS_TRAVELED_DIST_IN_TIME
     */
    private double getDiffToPredictedLocationAndBulletsTraveledDist(
        Point2D myCurLocation, 
        Point2D targetCurLocation, 
        double targetsHeading, 
        double targetsVelocity, 
        double time, 
        double velocityOfBullet
        )
    {
        Point2D predictedLocation = predictNewLocation(targetCurLocation, targetsHeading, targetsVelocity, time);
        double xLocDiff = (predictedLocation.getX() - myCurLocation.getX());
        double yLocDiff = (predictedLocation.getY() - myCurLocation.getY());
        logger.log("\txLocDiff: " + xLocDiff);
        logger.log("\tyLocDiff: " + yLocDiff);
        logger.log("\tMath.sqrt(("+xLocDiff+"*"+xLocDiff+") + ("+yLocDiff+"*"+yLocDiff+")): "/* + Math.sqrt((xLocDiff*xLocDiff) + (yLocDiff*yLocDiff))*/);
        logger.log(Level.FINE, "\t("+velocityOfBullet +"*"+ time+"): " + (velocityOfBullet * time)/* + " : " + 
                (Math.sqrt((xLocDiff*xLocDiff) + (yLocDiff*yLocDiff)) - (velocityOfBullet * time))*/);
        return Math.sqrt((xLocDiff*xLocDiff) + (yLocDiff*yLocDiff)) - (velocityOfBullet * time);
    }
    
    private double velocityOfBullet(double powerOfBullet)
    {
        return 20.0 - (3 * powerOfBullet);
    }    


}